﻿using Aspose.Cells.Drawing;
using DocumentFormat.OpenXml.Drawing.Charts;
using DocumentFormat.OpenXml.Wordprocessing;
using FFMpegCore;
using HashLib4CSharp.Base;
using LF.Domain.Services;
using Microsoft.Extensions.DependencyInjection;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Utilities;
using SkiaSharp;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.IO;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using WkHtmlToPdfDotNet;

namespace LF.Application.Helpers
{
    public class FileHelper
    {
        const int _maxKB = 512 * 1024;
        const int _oneKB = 25 * 1024;
        /// <summary>
        /// 获取文件MD5
        /// </summary>
        /// <param name="stream">file stream</param>
        /// <returns></returns>
        public static async Task<string?> Md5(Stream stream)
        {
            //https://github.com/uranium62/xxHash
            string? result = null;
            if (stream == null) return result;
            var size = stream.Length;
            var max = size > _maxKB ? _maxKB : _oneKB;
            var count = size % max == 0 ? size / max : (size / max + 1);
            var hash = HashFactory.Crypto.CreateMD5();
            hash.Initialize();

            stream.Seek(0, SeekOrigin.Begin);
            for (var i = 0; i < count; i++)
            {
                var slice = (int)(i == (count - 1) ? size - i * max : max);

                var buffer = new byte[slice];
                await stream.ReadAsync(buffer, 0, slice);
                hash.TransformByteSpan(buffer.AsSpan());
                await Task.Delay(1);
            }

            result = hash.TransformFinal().ToString();
            return result;
        }


        public static Stream? Get(string md5, bool isTemp = false)
        {
            //如果微信图片直接返回完整文件
            var weixinPath = Path.Combine(LFConfig.FilePath.Path, "weixin", md5 + ".png");
            if (File.Exists(weixinPath))
            {
                var file = new FileStream(weixinPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                file.Seek(0, SeekOrigin.Begin);
                return file;
            }
            //如果是服务报告
            var reportPath = Path.Combine(LFConfig.FilePath.Path, "servicereport", md5 + ".pdf");
            if (File.Exists(reportPath))
            {
                var file = new FileStream(reportPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                file.Seek(0, SeekOrigin.Begin);
                return file;
            }
            //服务端文件返回md5文件夹下所有分片汇总文件
            var path = Path.Combine(isTemp ? LFConfig.FilePath.Temp : LFConfig.FilePath.Path, md5);
            if (!Directory.Exists(path)) return null;
            var files = Directory.GetFiles(path).OrderBy(p => p);
            MemoryStream stream = new MemoryStream();
            {
                foreach (var chunk in files)
                {
                    if (chunk.IsNullOrEmpty()) continue;
                    using (var fs = new FileStream(chunk, FileMode.Open, FileAccess.Read, FileShare.Read))
                    {
                        var buffer = new byte[fs.Length];
                        fs.Read(buffer, 0, buffer.Length);
                        stream.Write(buffer, 0, buffer.Length);
                    }
                }
                stream.Seek(0, SeekOrigin.Begin);
            }
            return stream;
        }

        public static void Remove(string md5, bool isTemp = false)
        {
            var path = Path.Combine(isTemp ? LFConfig.FilePath.Temp : LFConfig.FilePath.Path, md5);
            if (!Directory.Exists(path)) return;
            Directory.Delete(path, true);
        }

        static object writing = true;
        /// <summary>
        /// 获取mp4
        /// </summary>
        /// <param name="md5"></param>
        /// <returns></returns>
        private static string? GetMp4(string md5)
        {
            var basepath = Path.Combine(LFConfig.FilePath.Path, "mp4");
            if (!Directory.Exists(basepath))
            {
                Directory.CreateDirectory(basepath);
            }

            var result = Path.Combine(basepath, $"{md5}.mp4");
            if (File.Exists(result))
            {
                return result;
            }
            var path = Path.Combine(LFConfig.FilePath.Path, md5);
            if (!Directory.Exists(path)) return null;
            lock (writing)
            {
                var files = Directory.GetFiles(path).OrderBy(p => p);
                using (var mp4 = File.Create(result))
                {
                    foreach (var chunk in files)
                    {
                        if (chunk.IsNullOrEmpty()) continue;
                        using (var fs = new FileStream(chunk, FileMode.Open, FileAccess.Read, FileShare.Read))
                        {
                            fs.CopyTo(mp4);
                        }
                    }

                }
            }

            return result;
        }
        /// <summary>
        /// 获取视频第一帧，全部径
        /// </summary>
        /// <param name="md5">视频文件</param>
        /// <returns></returns>
        public static string FirstFrame(string md5)
        {
            var mp4 = GetMp4(md5);
            if (string.IsNullOrEmpty(mp4))
            {
                throw new IOException("文件不存在!");
            }
            var result = Path.Combine(LFConfig.FilePath.Path, "mp4", $"{md5}.png");

            if (File.Exists(result))
            {
                return result;
            }
            //创建首帧图片
            var snapshot = FFMpeg.Snapshot(mp4, result, captureTime: TimeSpan.FromSeconds(0));

            return snapshot ? string.Empty : result;
        }

        #region 图片+小程序码合成

        public static async Task<byte[]> GetQrDviceImage(string macId)
        {
            using (var stream = await WeixinHelper.GetQrCodeAsync("customerproduct/index", macId, false))
            {
                var bytes = new byte[stream!.Length];
                stream.Read(bytes, 0, bytes.Length);

                using (var paint = new SKPaint())
                using (var image = SKBitmap.Decode(bytes))
                using (var bitmap = new SKBitmap(image.Width,image.Height))
                using (var canvas = new SKCanvas(bitmap))
                using (var path = new SKPath())
                {

                    path.AddCircle(image.Width / 2, image.Width / 2, image.Height / 2);
                    canvas.ClipPath(path);
                    canvas.DrawBitmap(image, new SKPoint(0, 0), paint);
                    //结果
                    var data = bitmap.Encode(SKEncodedImageFormat.Png, 100);
                    var result = data.ToArray();

                    return result;
                }

            }
        }

        /// <summary>
        /// 返回 活动码 300* 300 灰度、透明太阳码
        /// </summary>
        /// <param name="number"></param>
        /// <returns></returns>
        public static async Task<byte[]> GetQrImage(string number)
        {
            var width = 300;
            using (var stream = await WeixinHelper.GetQrCodeAsync("index/cityactivity_detail", number))
            {
                var bytes = new byte[stream!.Length];
                stream.Read(bytes, 0, bytes.Length);

                //灰色滤镜
                var grayFilter = SKColorFilter.CreateColorMatrix(new float[]
                {
                    0.299f, 0.587f, 0.114f, 0, 0,
                    0.299f, 0.587f, 0.114f, 0, 0,
                    0.299f, 0.587f, 0.114f, 0, 0,
                    0, 0, 0, 1, 0
                });
                using (var grayImageFilter = SKImageFilter.CreateColorFilter(grayFilter))
                using (var paint = new SKPaint())
                using (var image = new SKBitmap(width, width))
                using (var canvas = new SKCanvas(image))
                using (var path = new SKPath())
                {
                    //canvas.Translate(width / 2, width / 2);
                    canvas.RotateDegrees(180, width / 2, width / 2);

                    var bitmap = ZoomImage(SKBitmap.Decode(bytes), width);
                    paint.ColorFilter = grayFilter;
                    path.AddCircle(width / 2, width / 2, width / 2);
                    canvas.ClipPath(path);
                    canvas.DrawBitmap(bitmap, new SKPoint(0, 0), paint);
                  
                    //结果
                    var data = image.Encode(SKEncodedImageFormat.Png, 100);
                    var result = data.ToArray();

#if DEBUG
                    var weixinPath = Path.Combine(LFConfig.FilePath.Path, "weixin", "qrcode" + ".png");
                    File.WriteAllBytes(weixinPath, result);
#endif

                    return result;
                }

            }
        }
        /// <summary>
        /// 文字绘制在图片底部
        /// </summary>
        /// <param name="md5">图片</param>
        /// <param name="number">订单号</param>
        /// <returns>图片</returns>
        public static async Task<byte[]?> GetImage(string md5, string number)
        {
            var file = Get(md5);
            if (file == null)
            {
                return null;
            }
            using (var bitmap = SKBitmap.Decode(file))
            {
                var width = 480;
                var height = 480;
                //等比压缩尺寸
                var bytes = bitmap.Encode(SKEncodedImageFormat.Png, 60).ToArray();
                if (bitmap.Width > width || bitmap.Height > height)
                {
                    bytes = ZoomImage(bitmap, height).Encode(SKEncodedImageFormat.Png, 60).ToArray(); ;
                }
                //灰色滤镜
                var grayFilter = SKColorFilter.CreateColorMatrix(new float[]
                {
                    0.299f, 0.587f, 0.114f, 0, 0,
                    0.299f, 0.587f, 0.114f, 0, 0,
                    0.299f, 0.587f, 0.114f, 0, 0,
                    0, 0, 0, 1, 0
                });
                //反色滤镜
                var inverterFilter = SKColorFilter.CreateColorMatrix(new float[20] {
                    -1f,  0f,  0f, 0f, 1f,
                    0f, -1f,  0f, 0f, 1f,
                    0f,  0f, -1f, 0f, 1f,
                    0f,  0f,  0f, 1f, 0f
                });

                using (var grayImageFilter = SKImageFilter.CreateColorFilter(grayFilter))
                using (var inverterImageFilter = SKImageFilter.CreateColorFilter(inverterFilter))
                using (var paint = new SKPaint())
                using (var image = new SKBitmap(width, height))
                {
                    using (var canvas = new SKCanvas(image))
                    using (var path = new SKPath())
                    {
                        //裁剪为圆形
                        path.AddCircle(width / 2, width / 2, width / 2);
                        canvas.ClipPath(path);

                        paint.ImageFilter = grayImageFilter;
                        //绘制用户上传图片
                        canvas.DrawBitmap(SKBitmap.Decode(bytes), new SKPoint(0, 0), paint);
                        var offset = 60;
                        using (var stream = await WeixinHelper.GetQrCodeAsync("index/cityactivity_detail", number, false))
                        {
                            if (stream != null)
                            {
                                var qrcode = new byte[stream.Length];
                                stream.Read(qrcode, 0, qrcode.Length);

                                //绘制太阳码
                                var qrImage = SKBitmap.Decode(qrcode);
                                qrImage = ZoomImage(qrImage, 80);
                                canvas.ClipPath(path);
                                canvas.DrawBitmap(qrImage, new SKPoint(width - qrImage.Width - offset, offset), paint);
                            }
                        }


                        //当前灰度结果
                        var data = image.Encode(SKEncodedImageFormat.Png, 100);
                        var result = data.ToArray();
#if DEBUG
                        var weixinPath = Path.Combine(LFConfig.FilePath.Path, "weixin", "xxx1" + ".png");
                        File.WriteAllBytes(weixinPath, result);
#endif
                        //返色绘制
                        canvas.Clear();
                        paint.ColorFilter = inverterFilter;
                        canvas.DrawBitmap(SKBitmap.Decode(data.ToArray()), new SKPoint(0, 0), paint);
                        //结果
                        data = image.Encode(SKEncodedImageFormat.Png, 100);
                        result = data.ToArray();
#if DEBUG
                        weixinPath = Path.Combine(LFConfig.FilePath.Path, "weixin", "xxx" + ".png");
                        File.WriteAllBytes(weixinPath, result);
#endif
                        return result;
                    }
                }
            }
        }
        /// <summary>
        /// 文字绘制
        /// </summary>
        /// <param name="text">文本</param>
        /// <returns>图片</returns>
        public static byte[]? GetImage(string text)
        {
            using (var image = new SKBitmap(ToPixel(5), ToPixel(3)))
            {
                DrawText(image, text);
                using var data = image.Encode(SKEncodedImageFormat.Png, 60);
                var result = data.ToArray();
                //var weixinPath = Path.Combine(LFConfig.FilePath.Path, "weixin", "xxx" + ".png");
                //File.WriteAllBytes(weixinPath, result);
                return result;
            }
        }

        /// <summary>
        /// 厘米转换成像素
        /// </summary>
        /// <param name="cm">厘米数</param>
        /// <returns></returns>
        private static int ToPixel(float cm)
        {
            return (int)(cm * 37.795276f);
        }
        /// <summary>
        /// 裁剪图片，指定区域
        /// </summary>
        /// <param name="skBitmap"></param>
        /// <param name="bounds"></param>
        /// <returns></returns>
        private static byte[] CutImage(SKBitmap skBitmap, SKRectI bounds)
        {

            using var pixmap = new SKPixmap(skBitmap.Info, skBitmap.GetPixels());

            var subset = pixmap.ExtractSubset(bounds);

            using var data = subset.Encode(SkiaSharp.SKPngEncoderOptions.Default);
            return data.ToArray();
        }
        /// <summary>
        /// 缩放图片
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="targetHeight">缩放高度</param>
        /// <returns></returns>
        private static SKBitmap ZoomImage(SKBitmap bitmap,int targetHeight)
        {
            // 计算缩放后的目标宽度，保持纵横比
            float aspectRatio = (float)bitmap.Height / bitmap.Width;
            int targetWidth = (int)(targetHeight / aspectRatio);

            // 创建缩放后的目标尺寸
            SKSizeI targetSize = new SKSizeI(targetWidth, targetHeight);
            // 创建缩放后的SKBitmap对象
            SKBitmap scaledBitmap = new SKBitmap(targetSize.Width, targetSize.Height);

            // 使用SKCanvas将原始图像绘制到缩放后的图像上
            using (SKCanvas canvas = new SKCanvas(scaledBitmap))
            {

                var path = new SKPath();
                path.AddCircle(targetHeight/2, targetHeight/2, targetHeight/2);
                canvas.ClipPath(path);
                // 使用SKPaint进行绘制设置，如保持纵横比、插值质量等
                using (SKPaint paint = new SKPaint())
                {
                    // 设置插值质量为高质量
                    paint.FilterQuality = SKFilterQuality.High;

                    // 计算绘制的矩形区域，保持纵横比
                    float scaledWidth = targetHeight / aspectRatio;
                    float scaledHeight = targetHeight;
                    float x = (targetSize.Width - scaledWidth) / 2;
                    float y = 0;
                    SKRect destRect = SKRect.Create(x, y, scaledWidth, scaledHeight);

                    // 绘制原始图像到缩放后的图像上
                    canvas.DrawBitmap(bitmap, destRect, paint);
                }
            }
            return scaledBitmap;
        }

        private static void DrawText(SKBitmap bitmap, string text)
        {
            //var size = bitmap.Width / 10;
            //初始化画布
            var canvas = new SKCanvas(bitmap);
            
            var paint = new SKPaint()
            {
                Color = SKColors.White,
                IsAntialias = false, // 抗锯齿
                Style = SKPaintStyle.Fill,
                IsStroke = false,
                StrokeWidth = 0,
                FakeBoldText = false,
                Typeface = SKTypeface.FromFile(Path.Combine(AppContext.BaseDirectory,"Config","simsun.ttf")),
                TextSize = 16f,
            };
            var paint2 = new SKPaint()
            {
                Color = SKColors.Black,
                IsAntialias = false, // 抗锯齿
                Style = SKPaintStyle.Fill,
                IsStroke = false,
                StrokeWidth = 0,
                FakeBoldText = false,
                Typeface = SKTypeface.FromFile(Path.Combine(AppContext.BaseDirectory, "Config", "simsun.ttf")),
                TextSize = 16f,
            };

            using (SKPath path = new SKPath())
            {
                //path.MoveTo(new SKPoint(0,0));

                //https://blog.csdn.net/qq_38690432/article/details/113391815 获取圆上点的坐标
                //path.CubicTo(new SKPoint(0, size * 1),
                //             new SKPoint(0, size * 5),
                //             new SKPoint(0, size * 10));

                // Get path length
                //SKPathMeasure pathMeasure = new SKPathMeasure(path, false, 1);

                // Find new text size
                //paint.TextSize = pathMeasure.Length / 16 * 10;

                var rect = SKRect.Empty;
                paint.MeasureText(text, ref rect);
                // Draw text on path
                //canvas.DrawTextOnPath(text, path, 0, 0, paint);

                canvas.DrawText(text, new SKPoint(0, bitmap.Height - rect.Height), paint2);
                canvas.DrawText(text, new SKPoint(2, bitmap.Height - rect.Height), paint2);
                canvas.DrawText(text, new SKPoint(0, bitmap.Height - rect.Height - 1), paint2);
                canvas.DrawText(text, new SKPoint(2, bitmap.Height - rect.Height - 1), paint2);
                canvas.DrawText(text, new SKPoint(1, bitmap.Height - rect.Height), paint);
            }
        }
        #endregion
        /// <summary>
        /// 基于html导出pdf
        /// </summary>
        public static byte[] ConvertHtmlToPdf(string htmlcontent)
        {
            return ConvertHtmlToPdf(htmlcontent, null);
        }

        /// <summary>
        /// 基于html导出pdf（带自定义页脚设置）
        /// </summary>
        /// <param name="htmlcontent">HTML内容</param>
        /// <param name="footerSettings">页脚设置，为null时使用默认设置</param>
        /// <returns>PDF字节数组</returns>
        public static byte[] ConvertHtmlToPdf(string htmlcontent, FooterSettings? footerSettings)
        {
            var converter = new BasicConverter(new PdfTools());
            // 绑定转换过程中处理事件日志
            converter.PhaseChanged += Converter_PhaseChanged;
            converter.ProgressChanged += Converter_ProgressChanged;
            converter.Finished += Converter_Finished;
            converter.Warning += Converter_Warning;
            converter.Error += Converter_Error;

            // 默认页脚设置 - 支持HTML内容
            // 默认页脚设置 - 使用简单但有效的方式
            var defaultFooterSettings = new FooterSettings
            {
                FontSize = 9,
                FontName = "Microsoft YaHei",
                Left = "上海亦邮自动化科技有限公司    www.eusystec.com",
                Right = "第 [page] 页 / 共 [toPage] 页",
                Line = true,
                Spacing = 5
            };


            // pdf 样式设置
            var doc = new HtmlToPdfDocument()
            {
                GlobalSettings = {
                    ColorMode = WkHtmlToPdfDotNet.ColorMode.Color,
                    Orientation = WkHtmlToPdfDotNet.Orientation.Portrait,
                    PaperSize = WkHtmlToPdfDotNet.PaperKind.A4,
                },
                Objects = {
                    new ObjectSettings() {
                        PagesCount = true,
                        HtmlContent = htmlcontent,
                        WebSettings = { DefaultEncoding = "utf-8",EnableIntelligentShrinking = true },
                        //HeaderSettings = { FontSize = 9, Right = "Page [page] of [toPage]", Line = false },
                        FooterSettings = footerSettings ?? defaultFooterSettings
                    }
                }
            };
            // 转换为二进制
            byte[] pdf = converter.Convert(doc);
            return pdf;
        }

        /// <summary>
        /// 基于url导出pdf
        /// </summary>
        public static byte[] ConvertUrlToPdf(string url)
        {

            var converter = new BasicConverter(new PdfTools());
            // 绑定转换过程中处理事件日志
            converter.PhaseChanged += Converter_PhaseChanged;
            converter.ProgressChanged += Converter_ProgressChanged;
            converter.Finished += Converter_Finished;
            converter.Warning += Converter_Warning;
            converter.Error += Converter_Error;
            // pdf 样式设置
            var doc = new HtmlToPdfDocument()
            {
                GlobalSettings = {
                    PaperSize = WkHtmlToPdfDotNet.PaperKind.A3, // 纸张类型
                    Orientation = WkHtmlToPdfDotNet.Orientation.Landscape,
                },
                // 支持多个网址
                Objects = {
                    new ObjectSettings()
                    {
                        Page =  "http://localhost:8105/#/pages/service-report/reporttemplate?workOrderId=52956f3a-d7bb-46da-bafc-c957c8c8a467&taskId=b66c5809-9ae6-44e4-b169-430b32beec27",
                    }
                }
            };

            // 转换为二进制
            byte[] pdf = converter.Convert(doc);
            return pdf;
        }


        /// <summary>
        /// 转换产生错误日志输出
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void Converter_Error(object? sender, WkHtmlToPdfDotNet.EventDefinitions.ErrorArgs e)
        {
            Console.WriteLine("[转换错误] {0}", e.Message);
        }
        /// <summary>
        /// 转换产生警告日志输出
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>

        private static void Converter_Warning(object? sender, WkHtmlToPdfDotNet.EventDefinitions.WarningArgs e)
        {
            Console.WriteLine("[警告] {0}", e.Message);
        }
        /// <summary>
        /// 转转完成日志输出
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void Converter_Finished(object? sender, WkHtmlToPdfDotNet.EventDefinitions.FinishedArgs e)
        {
            Console.WriteLine("转换 {0} ", e.Success ? "成功" : "失败");
        }
        /// <summary>
        /// 转换进度日志输出
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void Converter_ProgressChanged(object? sender, WkHtmlToPdfDotNet.EventDefinitions.ProgressChangedArgs e)
        {
            Console.WriteLine("转换进度 {0}", e.Description);
        }
        /// <summary>
        /// 转换阶段日志输入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void Converter_PhaseChanged(object? sender, WkHtmlToPdfDotNet.EventDefinitions.PhaseChangedArgs e)
        {
            Console.WriteLine("阶段进度 {0} - {1}", e.CurrentPhase, e.Description);
        }

        /// <summary>
        /// 从配置文件获取网址并添加workOrderId和taskId参数，然后生成PDF文件
        /// </summary>
        /// <param name="workOrderId">工单ID</param>
        /// <param name="taskId">任务ID</param>
        /// <param name="serviceProvider">服务提供者，用于获取参数配置服务</param>
        /// <returns>PDF文件的字节数组</returns>
        public static byte[] ConvertConfigUrlToPdf(string workOrderId, string taskId, IServiceProvider serviceProvider)
        {
            // 获取参数配置服务
            var paramConfigService = serviceProvider.GetRequiredService<IParamConfigManagerService>();
            
            // 从配置中获取PDF生成的基础URL
            var urlConfig = paramConfigService.GetByKey("service_report_url");
            if (urlConfig == null || string.IsNullOrEmpty(urlConfig.Value))
            {
                throw new InvalidOperationException("未找到PDF报告URL配置，请在参数配置中添加key为'service_report_url'的配置项");
            }

            // 构建完整的URL，添加workOrderId和taskId参数
            var baseUrl = urlConfig.Value.TrimEnd('/');
            var finalUrl = $"{baseUrl}?workOrderId={workOrderId}&taskId={taskId}";

            // 使用现有的ConvertUrlToPdf方法生成PDF
            return ConvertUrlToPdf(finalUrl);
        }

        /// <summary>
        /// 从配置文件获取网址并添加workOrderId参数，然后生成PDF文件
        /// </summary>
        /// <param name="workOrderId">工单ID</param>
        /// <param name="serviceProvider">服务提供者，用于获取参数配置服务</param>
        /// <returns>PDF文件的字节数组</returns>
        public static byte[] ConvertConfigUrlToPdf(string workOrderId, IServiceProvider serviceProvider)
        {
            // 获取参数配置服务
            var paramConfigService = serviceProvider.GetRequiredService<IParamConfigManagerService>();
            
            // 从配置中获取PDF生成的基础URL
            var urlConfig = paramConfigService.GetByKey("pdf_report_url");
            if (urlConfig == null || string.IsNullOrEmpty(urlConfig.Value))
            {
                throw new InvalidOperationException("未找到PDF报告URL配置，请在参数配置中添加key为'pdf_report_url'的配置项");
            }

            // 构建完整的URL，添加workOrderId参数
            var baseUrl = urlConfig.Value.TrimEnd('/');
            var finalUrl = $"{baseUrl}?workOrderId={workOrderId}";

            // 使用现有的ConvertUrlToPdf方法生成PDF
            return ConvertUrlToPdf(finalUrl);
        }


    }
}
